/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.openejb.util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; public class PojoSerialization implements Serializable { private static final byte CLASS = (byte) 50; private static final byte FIELD = (byte) 51; private static final byte DONE = (byte) 52; private Object object; // sun.misc.Unsafe unsafe; private static final Object unsafe; private static final Method allocateInstance; private static final Method objectFieldOffset; private static final Method putInt; private static final Method putLong; private static final Method putShort; private static final Method putChar; private static final Method putByte; private static final Method putFloat; private static final Method putDouble; private static final Method putBoolean; private static final Method putObject; static { final Class<?> unsafeClass; try { unsafeClass = AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { public Class<?> run() { try { return Thread.currentThread().getContextClassLoader().loadClass("sun.misc.Unsafe"); } catch (final Exception e) { try { return ClassLoader.getSystemClassLoader().loadClass("sun.misc.Unsafe"); } catch (final ClassNotFoundException e1) { throw new IllegalStateException("Cannot get sun.misc.Unsafe", e); } } } }); } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe class", e); } unsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { try { final Field field = unsafeClass.getDeclaredField("theUnsafe"); field.setAccessible(true); return field.get(null); } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe", e); } } }); allocateInstance = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("allocateInstance", Class.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.allocateInstance", e); } } }); objectFieldOffset = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.objectFieldOffset", e); } } }); putInt = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putInt", Object.class, long.class, int.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putInt", e); } } }); putLong = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putLong", e); } } }); putShort = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putShort", Object.class, long.class, short.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putShort", e); } } }); putChar = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putChar", Object.class, long.class, char.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putChar", e); } } }); putByte = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putByte", Object.class, long.class, byte.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putByte", e); } } }); putFloat = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putFloat", Object.class, long.class, float.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putFloat", e); } } }); putDouble = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putDouble", Object.class, long.class, double.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putDouble", e); } } }); putBoolean = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putBoolean", Object.class, long.class, boolean.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putBoolean", e); } } }); putObject = AccessController.doPrivileged(new PrivilegedAction<Method>() { public Method run() { try { final Method mtd = unsafeClass.getDeclaredMethod("putObject", Object.class, long.class, Object.class); mtd.setAccessible(true); return mtd; } catch (final Exception e) { throw new IllegalStateException("Cannot get sun.misc.Unsafe.putObject", e); } } }); } /** * Constructor for externalization */ public PojoSerialization() { } public PojoSerialization(final Object object) { this.object = object; } private void writeObject(final ObjectOutputStream out) throws IOException { write(out); } private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { read(in); } protected Object readResolve() throws ObjectStreamException { return object; } protected void read(final ObjectInput in) throws IOException, ClassNotFoundException { byte b = in.readByte(); if (b != CLASS) { throw new IOException("Expected 'CLASS' byte " + CLASS + ", got: " + b); } Class clazz = (Class) in.readObject(); final Object object; try { object = allocateInstance.invoke(unsafe, clazz); } catch (final Exception e) { throw (IOException) new IOException("Cannot construct " + clazz.getName()).initCause(e); } while (b != DONE) { b = in.readByte(); switch (b) { case FIELD: { final String fieldName = in.readUTF(); final Object value = in.readObject(); Field field = null; try { field = clazz.getDeclaredField(fieldName); } catch (final NoSuchFieldException e) { throw (IOException) new IOException("Cannot find field " + fieldName).initCause(e); } setValue(field, object, value); } break; case CLASS: { clazz = (Class) in.readObject(); } break; } } this.object = object; } protected void write(final ObjectOutput out) throws IOException { final List<Class> classes = new ArrayList<Class>(); Class c = object.getClass(); while (c != null && !c.equals(Object.class)) { classes.add(c); c = c.getSuperclass(); } for (final Class clazz : classes) { out.writeByte(CLASS); out.writeObject(clazz); final Field[] fields = clazz.getDeclaredFields(); for (final Field field : fields) { if (Modifier.isStatic(field.getModifiers())) { continue; } if (Modifier.isTransient(field.getModifiers())) { continue; } field.setAccessible(true); out.writeByte(FIELD); out.writeUTF(field.getName()); try { out.writeObject(field.get(object)); } catch (final IllegalAccessException e) { throw (IOException) new IOException("Cannot write field " + field.getName()).initCause(e); } } } out.writeByte(DONE); } private void setValue(final Field field, final Object object, final Object value) { final long offset; try { offset = (Long) objectFieldOffset.invoke(unsafe, field); } catch (final Exception e) { throw new IllegalStateException("Failed getting offset for: field=" + field.getName() + " class=" + field.getDeclaringClass().getName(), e); } final Class type = field.getType(); try { if (type.isPrimitive()) { if (type.equals(Integer.TYPE)) { putInt.invoke(unsafe, object, offset, ((Integer) value).intValue()); } else if (type.equals(Long.TYPE)) { putLong.invoke(unsafe, object, offset, ((Long) value).longValue()); } else if (type.equals(Short.TYPE)) { putShort.invoke(unsafe, object, offset, ((Short) value).shortValue()); } else if (type.equals(Character.TYPE)) { putChar.invoke(unsafe, object, offset, ((Character) value).charValue()); } else if (type.equals(Byte.TYPE)) { putByte.invoke(unsafe, object, offset, ((Byte) value).byteValue()); } else if (type.equals(Float.TYPE)) { putFloat.invoke(unsafe, object, offset, ((Float) value).floatValue()); } else if (type.equals(Double.TYPE)) { putDouble.invoke(unsafe, object, offset, ((Double) value).doubleValue()); } else if (type.equals(Boolean.TYPE)) { putBoolean.invoke(unsafe, object, offset, ((Boolean) value).booleanValue()); } else { throw new IllegalStateException("Unknown primitive type: " + type.getName()); } } else { putObject.invoke(unsafe, object, offset, value); } } catch (final Exception e) { e.printStackTrace(); } } }